/*
 * Copyright (c) 2011.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.skavookie.expressions

/**
 * Created by IntelliJ IDEA.
 * User: joshua
 * Date: 5/24/11
 * Time: 1:32 PM
 * To change this template use File | Settings | File Templates.
 */

/**
 * This is the default visitor.  It impliments all the standard visitors.  Attached to it is a registry that it
 * uses to implement get and set expressions.  You can pass the constructor a registry of your own, or it will
 * provide one.
 */
class DefaultExprVisitor(protected val registry: Registry) extends NumericExprVisitor
		with IntegralExprVisitor with FractionalExprVisitor {
	def this() = this (Registry())

	def literal[T](expr: TLiteral[T]): Expr[T] = expr

	def get[T](expr: TGet[T]): Expr[T] = LiteralExpr(registry.get[T](expr.key)(expr.getManifest).get)(expr.getManifest)

	def set[T](expr: TSet[T]): Expr[T] = visit(expr.value) match {
		case l: TLiteral[_] => {
			registry.register(expr.key, l.literal)(expr.getManifest)
			LiteralExpr(l.literal)(expr.getManifest)
		}
		case x => SetExpr(expr.key, x)(expr.getManifest)
	}

	def plus[T](expr: PlusExpr[T]): Expr[T] = {
		val lhs = visit(expr.lhs)
		val rhs = visit(expr.rhs)
		(lhs, rhs) match {
			case (l: TLiteral[_], r: TLiteral[_]) => LiteralExpr(expr.getNumeric.plus(l.literal, r.literal))(l.getManifest)
			case _ => PlusExpr(lhs, rhs)(expr.getNumeric)
		}
	}

	def minus[T](expr: MinusExpr[T]): Expr[T] = {
		val lhs = visit(expr.lhs)
		val rhs = visit(expr.rhs)
		(lhs, rhs) match {
			case (l: TLiteral[_], r: TLiteral[_]) => LiteralExpr(expr.getNumeric.minus(l.literal, r.literal))(l.getManifest)
			case _ => MinusExpr(lhs, rhs)(expr.getNumeric)
		}
	}

	def times[T](expr: TimesExpr[T]): Expr[T] = {
		val lhs = visit(expr.lhs)
		val rhs = visit(expr.rhs)
		(lhs, rhs) match {
			case (l: TLiteral[_], r: TLiteral[_]) => LiteralExpr(expr.getNumeric.times(l.literal, r.literal))(l.getManifest)
			case _ => TimesExpr(lhs, rhs)(expr.getNumeric)
		}
	}

	def negate[T](expr: NegateExpr[T]): Expr[T] = visit(expr.x) match {
		case x: TLiteral[_] => LiteralExpr(expr.getNumeric.negate(x.literal))(x.getManifest)
		case x => NegateExpr(x)(expr.getNumeric)
	}

	def quot[T](expr: QuotExpr[T]): Expr[T] = {
		val lhs = visit(expr.lhs)
		val rhs = visit(expr.rhs)
		(lhs, rhs) match {
			case (l: TLiteral[_], r: TLiteral[_]) => LiteralExpr(expr.getIntegral.quot(l.literal, r.literal))(l.getManifest)
			case _ => QuotExpr(lhs, rhs)(expr.getIntegral)
		}
	}

	def rem[T](expr: RemExpr[T]): Expr[T] = {
		val lhs = visit(expr.lhs)
		val rhs = visit(expr.rhs)
		(lhs, rhs) match {
			case (l: TLiteral[_], r: TLiteral[_]) => LiteralExpr(expr.getIntegral.rem(l.literal, r.literal))(l.getManifest)
			case _ => RemExpr(lhs, rhs)(expr.getIntegral)
		}
	}

	def div[T](expr: DivExpr[T]): Expr[T] = {
		val lhs = visit(expr.lhs)
		val rhs = visit(expr.rhs)
		(lhs, rhs) match {
			case (l: TLiteral[_], r: TLiteral[_]) => LiteralExpr(expr.getFractional.div(l.literal, r.literal))(l.getManifest)
			case _ => DivExpr(lhs, rhs)(expr.getFractional)
		}
	}
}
